﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Infraestruturex.Automata.SPA;
using Infraestruturex;
using Infraestruturex.BaseLibrary;
using Compilex.Syntatic.Declarations;

namespace Compilex.Syntatic
{
    public class SemanticActionHooks
    {
        public SemanticActionManager SemanticActionManager { get; set; }
        public SyntaticContext Context { get; set; }
        

        public SemanticActionHooks(SemanticActionManager manager, SyntaticContext context)
        {
            this.SemanticActionManager = manager;
            this.Context = context;
            InjectHooks();
        }

        public void InjectHooks()
        {
            // ação padrão: push
            string[] keys = SemanticActionManager.SemanticActionTable.Keys.ToArray();
            keys.ForEach(
                action => SemanticActionManager.SemanticActionTable[action] = Context.PushCurrentToken);

            /***** DECLARAÇÃO DE VARIÁVEL ******/
            // declaração de variável
            Override(Context.VariableDeclaration.StartFinishVariable, "S8", "S10");
            
            Override(Context.FunctionDeclaration.StartFinishLocalVariable, "S81", "S83", "S88");

            // declaração de arrray
            Override(Context.VariableDeclaration.StartFinishArray, "S36");

            
            Override(Context.FunctionDeclaration.StartFinishLocalArray, "S122");

            // ver se array está recebendo valor padrão
            Override(Context.VariableDeclaration.AddDefaultValue, "S13");

            // erro: tipo que não é cadeia recebendo valor padrão = cadeiex
            Override(() => { throw new SyntaticException("Inicialização inválida"); }, "S19");


            /***** DECLARAÇÃO DE FUNÇÃO *****/
            // criar uma nova declaração de função
            Override(Context.FunctionDeclaration.Start, "S6");

            // identificador da função
            Override(Context.FunctionDeclaration.AddName, "S11");

            // abrir novo escopo
            Override(() => { Context.ScopeManager.OpenScope();
            Context.CommandBlockStack.StartCommandBlock();
            }, "S17");

            // trata um argumento (variável normal) de função
            Override(Context.FunctionDeclaration.AddParameter, "S37");

            // trata um argumento (array) de função
            Override(Context.FunctionDeclaration.AddArrayParamter, "S70");


            // não é void, bota o tipo da função
            Override(Context.FunctionDeclaration.AddReturnType,
                "S50", "S51", "S52", "S53");
            
            // corpo da função terminou
            Override(Context.FunctionDeclaration.EndFunction, "S48");

            /***** ATRIBUIÇÃO ******/
            // começo com variável simples
            Override(Context.Assignment.StartOrdinaryVariableLHS, "S87", "S95");

            // lhs com array
            Override(Context.Assignment.StartArrayLHS, "S124");

            // retorno de b_expression
            Override(Context.Assignment.EndBExpression, "S106", "S21");

            // declaração com caractere
            Override(Context.Assignment.RHSCharacter, "S105", "S20");

            // declaração com literal
            Override(Context.Assignment.RHSLiteral, "S104");

            /***** EXPRESSÕES BOOLEANAS *****/
            Override(Context.ExpressionStack.ArithmeticExpressionEnded, "S136", "S147",
                "S152");

            Override(Context.ExpressionStack.AddBooleanLiteral, "S134", "S135", "S145", "S146");

            Override(Context.ExpressionStack.AddBooleanOperator, 
                "S133", "S143", "S144", "S150", "S151");

            Override(Context.ExpressionStack.AddEqualityOperator,
                "S141", "S142", "S148", "S149");

            Override(Context.ExpressionStack.AddComparisonOperator,
                "S137", "S138", "S139", "S140");

            Override(Context.ExpressionStack.StartNewExpression,
                "S157", "S160", "S183");
            
            /***** EXPRESSÕES ARITIMÉTICAS *****/
            // operando é variável simples
            Override(() =>
                {
                    string nextToken = Context.TokenProvider.TryPeekNextNextTokenLexeme();
                    if (nextToken == "(" || nextToken == "[" || nextToken == ".")
                    {
                        Context.PushCurrentToken();
                        return;
                    };
                    Context.ExpressionStack.AddSimpleIdOperand();
                    
                }, "S156", "S159");

            // operando é constante
            Override(Context.ExpressionStack.AddConstantOperand, "S155", "S158");

            // operador
            Override(Context.ExpressionStack.AddArithmeticOperator,
                "S161", "S162", "S163", "S164", "S166", "S167", "S171", "S172", "S154", "S153");


            // b_expressao retornou
            Override(Context.ExpressionStack.BExpressionEnded,
                "S174");

            Override(() =>
                {
                    if (Context.ExpressionStack.Count > 0)
                        throw new InternalSyntaticException("Expressoes nao foram fechadas.");
                },
            "S110");


            /***** CHAMADA DE FUNÇÃO *****/
            // abriu parenteses - chamada de função como expressão
            Override(() =>
                {
                    //Context.ExpressionStack.StartNewExpression();
                    Context.ExpressionStack.StartFunctionCall();
                    Context.ExpressionStack.StartNewExpression();
                }, "S168");

            // abriu parenteses - chamada de função como comando
            Override(() =>
                {
                    Context.FunctionCallStack.StartFunctionCall();
                    Context.ExpressionStack.StartNewExpression();
                }, "S86");

            // leu operando
            Override(Context.FunctionCallStack.ReadBExpressionParameter, "S178", "S187", "S103", "S127");

            Override(Context.FunctionCallStack.ReadLiteralParameter, "S100", "S125");



            // fechou parenteses - chamada de função como expressão
            Override(() =>
            {
                Context.ExpressionStack.CancelExpression();
                Context.ExpressionStack.EndFunctionCall();
            }, "S177");
            Override(() =>
            {
                Context.ExpressionStack.EndFunctionCall();
                //Context.ExpressionStack.EndExpression();
            }, "S182");

            // fechou parenteses - chamada de função como comando
            Override(() =>
            {
                Context.ExpressionStack.CancelExpression();
                Context.FunctionCallStack.EndFunctionCall();
            }, "S102");
            Override(Context.FunctionCallStack.EndFunctionCall, "S114");

            // retorno de função
            Override(Context.ExpressionStack.StartNewExpression, "S80");
            Override(Context.FunctionDeclaration.ReturnFunctionBExpression, "S94");
            Override(() =>
            {
                Context.ExpressionStack.CancelExpression();
                Context.FunctionDeclaration.ReturnVoid();
            }, "S93");


            /***** SEX ******/
            // abre um novo bloco de comando em "sex"
            Override(() =>
            {
                Context.ExpressionStack.StartNewExpression();
                Context.CommandBlockStack.StartCommandBlock();
            }, "S78");

            // no fim da bexpressao
            Override(() =>
                {
                    Context.ExpressionStack.EndExpression();
                    Context.CommandBlockStack.StartIf();
                }, "S89");

            // no "senex"
            Override(Context.CommandBlockStack.If_Else, "S117");

            // no "finex"
            Override(Context.CommandBlockStack.EndIf, "S118", "S131");
            // no "sex"
            Override(Context.CommandBlockStack.EndCommandBlock, "S129");

            /***** ENQUANTEX *****/
            // antes da expressão
            Override(() =>
            {
                Context.ExpressionStack.StartNewExpression();
                Context.CommandBlockStack.StartCommandBlock();
                Context.CommandBlockStack.StartWhileBeforeExpression();
            }, "S79");

            // no fim da bexpressao
            Override(() =>
                {
                    Context.ExpressionStack.EndExpression();
                    Context.CommandBlockStack.AfterWhileExpression();
                }, "S90");

            // no "finex"
            Override(Context.CommandBlockStack.EndWhile, "S120");
            // no "enquantex"
            Override(Context.CommandBlockStack.EndCommandBlock, "S130");

            /**** VETOR ****/
            // vetor como expressão
            Override(Context.ExpressionStack.AddArrayElementOperand, "S179");
        }

        private void Override(SemanticAction action, params string[] names)
        {
            names.ForEach(name => SemanticActionManager[name] = action);
        }
    }
}
